﻿using Microsoft.VisualBasic.Devices;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace ServerCrafter.ClassLibrary
{
    public static class Info
    {
        /// <summary>
        /// Gets information about the system.
        /// </summary>
        public static class System
        {
            // Background workers
            private static BackgroundWorker bgWorkerGerPortStatus;

            private static BackgroundWorker bgWorkerGetIpAddress;

            // Public variables whose values have been assigned by background workers
            public static string IpAddress;

            public static bool IsPortOpen;

            public static ulong TotalRamInMb;
            public static ulong TotalRamInKb;

            /// <summary>
            /// Runs the background worker that gets the IP address.
            /// </summary>
            public static void GetIpAddress()
            {
                bgWorkerGetIpAddress = new BackgroundWorker();
                Logger.WriteInfo("IP address requested. Starting the background worker that get the IP address.");
                bgWorkerGetIpAddress.DoWork += bgWorkerGetIpAddress_DoWork;
                if (!bgWorkerGetIpAddress.IsBusy) bgWorkerGetIpAddress.RunWorkerAsync();
            }

            /// <summary>
            /// Handles the DoWork event of the bgWorkerGetIpAddress control. Gets the IP address.
            /// </summary>
            /// <param name="sender">The source of the event.</param>
            /// <param name="e">The <see cref="DoWorkEventArgs"/> instance containing the event data.</param>
            /// <exception cref="NotImplementedException"></exception>
            private static void bgWorkerGetIpAddress_DoWork(object sender, DoWorkEventArgs e)
            {
                Logger.WriteInfo("Background worker started. Creating web request.");
                var request = (HttpWebRequest)WebRequest.Create("http://ifconfig.me");
                request.UserAgent = "curl"; // Simulates "curl" Linux command
                request.Method = "GET";
                string ipAddress = null;

                try
                {
                    using (var response = request.GetResponse())
                    {
                        using (var reader = new StreamReader(response.GetResponseStream()))
                        {
                            ipAddress = reader.ReadToEnd().Replace("\n", "");
                        }
                    }
                    Logger.WriteInfo("IP address acquired: " + ipAddress);
                }
                catch (IOException exception)
                {
                    Logger.WriteWarning(
                        "Could not get IP address! The stream is already in use by a previous call to BeginGetResponse.");
                    Logger.WriteWarning("\tException: " + exception);
                }
                catch (ProtocolViolationException exception)
                {
                    Logger.WriteWarning(
                        "Could not get the IP address! Method is GET or HEAD, and either ContentLength is greater or equal to zero or SendChunked is true; or the HttpWebRequest has an entity body but the GetResponse method is called without calling the GetRequestStream method; or the ContentLength is greater than zero, but the application does not write all of the promised data.");
                    Logger.WriteWarning("\tException: " + exception);
                }
                catch (NotSupportedException exception)
                {
                    Logger.WriteWarning(
                        "Could not get the IP address! The request cache validator indicated that the response for this request can be served from the cache; however, this request includes data to be sent to the server. Requests that send data must not use the cache.");
                    Logger.WriteWarning("\tException: " + exception);
                }
                catch (WebException exception)
                {
                    Logger.WriteWarning(
                        "Could not get the IP address! The time-out period for the request expired; or an error occurred while processing the request.");
                    Logger.WriteWarning("\tException: " + exception);
                }
                catch (Exception exception)
                {
                    Logger.WriteWarning("Could not get IP address! An unknown exception has occured.");
                    Logger.WriteWarning("\tException: " + exception);
                }

                IpAddress = ipAddress;
            }

            /// <summary>
            /// Runs the background worker thta gets the port status.
            /// </summary>
            public static void GetPortStatus()
            {
                bgWorkerGerPortStatus = new BackgroundWorker();
                Logger.WriteInfo("Server port status requested. Starting the background worker that get the server port status.");
                bgWorkerGerPortStatus.DoWork += bgWorkerGerPortStatus_DoWork;
                if (!bgWorkerGerPortStatus.IsBusy) bgWorkerGerPortStatus.RunWorkerAsync();
            }

            /// <summary>
            /// Handles the DoWork event of the bgWorkerGerPortStatus control. Gets the port status.
            /// </summary>
            /// <param name="sender">The source of the event.</param>
            /// <param name="e">The <see cref="DoWorkEventArgs"/> instance containing the event data.</param>
            private static void bgWorkerGerPortStatus_DoWork(object sender, DoWorkEventArgs e)
            {
                Logger.WriteInfo("Background worker started. Checking port status.");
                try
                {
                    var tcpClient = new TcpClient();
                    tcpClient.Connect(IpAddress, Server.GetPortNumber());

                    Logger.WriteInfo("Port is open.");
                    IsPortOpen = true;
                }
                catch (ArgumentNullException exception)
                {
                    Logger.WriteWarning("Could not get port status! The hostname parameter is null.");
                    Logger.WriteWarning("\tException: " + exception);
                }
                catch (ArgumentOutOfRangeException exception)
                {
                    Logger.WriteWarning(
                        "Could not get port status. The port parameter is not between MinPort and MaxPort.");
                    Logger.WriteWarning("\tException: " + exception);
                }
                catch (SocketException exception)
                {
                    Logger.WriteInfo("Port is closed. Reason: " + exception);
                    IsPortOpen = false;
                }
            }

            /// <summary>
            /// Gets the total system RAM.
            /// </summary>
            /// <returns></returns>
            public static void GetTotalRamInMB()
            {
                var totalPhysicalMemory = new ComputerInfo().TotalPhysicalMemory;
                TotalRamInMb = totalPhysicalMemory / (1024 * 1024);
            }

            public static void GetTotalRamInKb()
            {
                var totalPhysicalMemory = new ComputerInfo().TotalPhysicalMemory;
                TotalRamInKb = totalPhysicalMemory * 1024;
            }

            public static class PerformanceCounters
            {
                public static class CPU
                {
                    private static PerformanceCounter pcServerCrafter = new PerformanceCounter("Process", "% Processor Time", Processes.GetName(Process.GetCurrentProcess().Id));
                    private static PerformanceCounter pcComputer = new PerformanceCounter("Processor", "% Processor Time", "_Total");
                    private static PerformanceCounter pcServer;

                    public static float ServerCrafterPercentage()
                    {
                        return pcServerCrafter.NextValue() / Environment.ProcessorCount;
                    }

                    public static float ComputerPercentage()
                    {
                        return pcComputer.NextValue() / Environment.ProcessorCount;
                    }

                    /// <summary>
                    /// Gets the server process CPU usage and returns it.
                    /// </summary>
                    /// <returns></returns>
                    public static float ServerPercentage()
                    {
                        if (!ClassLibrary.Server.IsRunning)
                        {
                            pcServer = null;
                            return 0;
                        }

                        if (pcServer == null)
                            pcServer = new PerformanceCounter("Process", "% Processor Time",
                                                            Processes.GetName(ClassLibrary.Server.Pid), true);

                        return pcServer.NextValue();
                    }
                }

                public static class Ram
                {
                    private static readonly PerformanceCounter pcComputer = new PerformanceCounter("Memory", "Available MBytes", true);
                    private static readonly PerformanceCounter PcServerCrafter = new PerformanceCounter("Process", "Working Set - Private", Processes.GetName(Process.GetCurrentProcess().Id), true);
                    private static PerformanceCounter pcServer;

                    public static float ComputerPercentage()
                    {
                        return ((TotalRamInMb - pcComputer.NextValue()) / TotalRamInMb) * 100;
                    }

                    public static float ServerCrafterPercentage()
                    {
                        return ((PcServerCrafter.NextValue() / (1024 * 1024)) / TotalRamInMb) * 100;
                    }

                    public static float ServerPercentage()
                    {
                        if (!ClassLibrary.Server.IsRunning)
                        {
                            pcServer = null;
                            return 0;
                        }

                        if (pcServer == null)
                            pcServer = new PerformanceCounter("Process", "Working Set - Private", Processes.GetName(ClassLibrary.Server.Pid), true);

                        return ((pcServer.NextValue()) / (1024 * 1024) / TotalRamInMb) * 100;
                    }
                }
            }

            private static class Processes
            {
                //public static string GetName(int pid)
                //{
                //    var processCategory = new PerformanceCounterCategory("Process");
                //    var runnedInstances = processCategory.GetInstanceNames();

                //    foreach (var runnedInstance in runnedInstances)
                //    {
                //        using (var performanceCounter =
                //            new PerformanceCounter("Process", "ID Process", runnedInstance, true))
                //        {
                //            if ((int) performanceCounter.RawValue == pid) return runnedInstance;
                //        }
                //    }
                //    return null;
                //}

                public static string GetName(int pid)
                {
                    var processName = Process.GetProcessById(pid).ProcessName;
                    var nameIndex = 1;
                    var value = processName;
                    var counterName = processName + "#" + nameIndex;
                    var pc = new PerformanceCounter("Process", "ID Process", counterName, true);

                    while (true)
                    {
                        try
                        {
                            if (pid == (int)pc.NextValue())
                            {
                                value = counterName;
                                break;
                            }
                            else
                            {
                                nameIndex++;
                                counterName = processName + "#" + nameIndex;
                                pc = new PerformanceCounter("Process", "ID Process", counterName, true);
                            }
                        }
                        catch (SystemException ex)
                        {
                            if (ex.Message == "Instance '" + counterName + "' does not exist in the specified Category.")
                            {
                                break;
                            }
                            else
                            {
                                throw;
                            }
                        }
                    }

                    return value;
                }
            }
        }

        /// <summary>
        /// Gets information about the server.
        /// </summary>
        public class Server
        {
            /// <summary>
            /// Gets the server port number.
            /// </summary>
            /// <returns></returns>
            public static int GetPortNumber()
            {
                // READ FROM ARRAY!
                using (var reader = new StreamReader(Settings.ServerStartInfo.WorkingDirectory + @"server.properties"))
                {
                    var portNumber = 0;
                    try
                    {
                        string line;
                        while ((line = reader.ReadLine()) != null)
                        {
                            var serverPort = line.Split('=')[1];
                            if (!line.Contains("server-port=")) continue;
                            try
                            {
                                portNumber = Convert.ToInt32(serverPort);
                            }
                            catch (FormatException exception)
                            {
                                Logger.WriteInfo("Server port \"" + serverPort + "\" is an invalid server port.");
                                Logger.WriteInfo("\tException: " + exception);
                            }
                            catch (OverflowException exception)
                            {
                                Logger.WriteInfo("Server port \"" + serverPort + "\" is an invalid server port.");
                                Logger.WriteInfo("\tException: " + exception);
                            }
                        }
                    }
                    catch (OutOfMemoryException exception)
                    {
                        Logger.WriteWarning(
                            "Could not read file! There is insufficient memory to allocate a buffer for the returned string.");
                        Logger.WriteWarning("\tException: " + exception);
                    }
                    catch (IOException exception)
                    {
                        Logger.WriteWarning("Could not read file! An I/O error occured.");
                        Logger.WriteWarning("\tException: " + exception);
                    }
                    catch (Exception exception)
                    {
                        Logger.WriteWarning("Could not read file or get port number! An unknown exception occured.");
                        Logger.WriteWarning("\tException: " + exception);
                    }

                    return portNumber;
                }
            }
        }
    }
}